home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Hot Mix 17
/
Hot Mix 17.iso
/
HM17_SGI
/
research
/
lib
/
ascii_template.pro
< prev
next >
Wrap
Text File
|
1997-07-08
|
47KB
|
1,421 lines
; $Id: ascii_template.pro,v 1.12 1997/04/22 21:54:29 mark Exp $
;
; Copyright (c) 1996-1997, Research Systems, Inc. All rights reserved.
; Unauthorized reproduction prohibited.
;+
; NAME:
; ASCII_TEMPLATE
;
; PURPOSE:
; Generate a template that defines an ASCII file format.
;
; CATEGORY:
; Input/Output.
;
; CALLING SEQUENCE:
; template = ASCII_TEMPLATE( [file] )
;
; INPUTS:
; file - Name of file to base the template on.
; Default = use DIALOG_PICKFILE to get the file.
;
; INPUT KEYWORD PARAMETERS:
; browse_lines - Number of lines to read in at a time via the
; GUI's browse button. Default = 50.
;
; OUTPUT KEYWORD PARAMETERS:
; cancel - Boolean indicating if the user canceled
; out of the interface (1B = canceled).
;
; OUTPUTS:
; The function returns a template (structure) defining ASCII files
; of the input file's format. Such templates may be used as inputs
; to function READ_ASCII. (0 is returned if the user canceled.)
;
; COMMON BLOCKS:
; None.
;
; SIDE EFFECTS:
; None.
;
; RESTRICTIONS:
; See DESCRIPTION.
;
; DESCRIPTION:
; This routine presents a graphical user interface (GUI) that assists
; the user in defining a template.
;
; ASCII files handled by this routine consist of an optional header
; of a fixed number of lines, followed by columnar data. Files may
; also contain comments, which exist between a user-specified comment
; string and the corresponding end-of-line.
;
; One or more rows of data constitute a "record." Each data element
; within a record is considered to be in a different column, or "field."
; Adjacent fields may be "grouped" into multi-column fields.
; The data in one field must be of, or promotable to, a single
; type (e.g., FLOAT).
;
; EXAMPLES:
; ; Generating a template to be used later, maybe on a set of files.
; template = ASCII_TEMPLATE()
;
; ; Same as above, but immediately specifying which file to use.
; template = ASCII_TEMPLATE(file)
;
; ; Same as above, but returning flag if the user canceled.
; template = ASCII_TEMPLATE(file, CANCEL=cancel)
;
; ; Generating and using a template in place for reading data.
; data = READ_ASCII(file, TEMPLATE=ASCII_TEMPLATE(file))
;
; DEVELOPMENT NOTES:
; - see ???,!!!,xxx in the code
; - errors preserving state when switch pages with 'back/next'
; - make NaN default missing value as in reader ?
;
; MODIFICATION HISTORY:
; AL & RPM, 8/96 - Written.
;
;-
;
; ROUTINES:
; fun at_build_templ - build a template
; pro at_delete_template - delete a template
; fun at_get_lines - read lines from file
; fun at_build_template - build a template
; fun at_num_fields -
; fun at_default_delimit -
; fun at_default_groups -
; fun at_which_field -
; fun at_str_to_val -
; fun at_list_to_str -
; fun at_str_to_list -
; pro at_remove_tabs -
; pro at_display_text -
; pro at_resize_table -
; pro at_sample_record -
; pro at_set_list -
; pro at_update -
; pro at_3_event -
; pro at_2_event -
; pro at_1_event -
; pro at_set_state -
; pro at_widget_cleanup -
; pro at_widget_event -
; fun at_widget - dialog to generate a template
; fun at_check_file - check validity of input file
; fun ascii_template - the main routine
; -----------------------------------------------------------------------------
;
; Purpose: Build an ASCII file template, using defaults as necessary.
;
function at_build_templ, $
num_fields=num_fields, $
field_types=field_types, $
field_names=field_names, $
field_locations=field_locations, $
record_start_loc=record_start_loc, $
delimiter=delimiter, $
groups=groups, $
missing_value=missing_value, $
comment_symbol=comment_symbol
if (n_elements(num_fields) eq 0) then num_fields = 1
tot_num_fields = total(num_fields)
if (n_elements(field_types) eq 0) then field_types = 4L
if (n_elements(field_locations) eq 0) then $
field_locations = lonarr(tot_num_fields)
if (n_elements(field_names) eq 0) then begin
digits_str = string(strlen(strtrim(string(fix(tot_num_fields)),2)))
fstr = '(i' + strtrim(digits_str,2) + '.' + strtrim(digits_str,2) + ')'
field_names = 'field' + string(indgen(tot_num_fields)+1,format=fstr)
endif
;
if (n_elements(record_start_loc) eq 0) then record_start_loc = 0L
if (n_elements(delimiter) eq 0) then delimiter = 0B
if (n_elements(missing_value) eq 0) then missing_value = 0.0
if (n_elements(groups) eq 0) then groups = indgen(tot_num_fields)
if (n_elements(comment_symbol) eq 0) then comment_symbol = ''
;
; define the ASCII template structure
;
fields = replicate({ascii_field_struct, name:'', type:0, loc:0L}, $
tot_num_fields)
if (tot_num_fields eq 1) then begin
fields.name = field_names(0)
fields.type = field_types(0)
fields.loc = field_locations(0)
endif else begin
fields.name = field_names
fields.type = field_types
fields.loc = field_locations
endelse
template = { $
record_start_loc: record_start_loc, $
delimit: delimiter(0), $
missing_value: float(missing_value), $ ; Why FLOAT() ???
comment_symbol: comment_symbol, $
p_num_fields: ptr_new(num_fields), $
p_fields: ptr_new(fields), $
p_groups: ptr_new(groups) $
}
return, template
end ; at_build_templ
; -----------------------------------------------------------------------------
;
; Purpose: Delete an ASCII file template.
;
pro at_delete_template, template
info = size(template)
if (info(info(0)+1) ne 8) then return
;
if (ptr_valid(template.p_num_fields)) then ptr_free, template.p_num_fields
if (ptr_valid(template.p_fields)) then ptr_free, template.p_fields
if (ptr_valid(template.p_groups)) then ptr_free, template.p_groups
end ; at_delete_template
; -----------------------------------------------------------------------------
;
; Purpose: Return a requested number of lines from the ASCII file.
; If skip is set, then skip the first skip number of lines before
; starting the read.
; Return the last_pos to resume reading from a given point and
; notify when the end of file has been reached.
;
function at_get_lines, name, num_lines, last_pos=last_pos, $
end_reached=end_reached, skip=skip
;
catch, error_status
if (error_status ne 0) then begin
end_reached = 1
if (n_elements(unit) gt 0) then free_lun, unit
return, ''
endif
;
if (n_elements(last_pos) eq 0) then last_pos = 0
if (n_elements(skip) eq 0) then skip = 0
lines = strarr(num_lines)
line = ''
count = 0L
openr, unit, name, /get_lun
point_lun, unit, last_pos
for i=0, skip-1 do readf, unit, line
while (not eof(unit) and count lt num_lines) do begin
readf, unit, line
lines(count) = line
count = count + 1
endwhile
;
end_reached = (count lt num_lines)
point_lun, -unit, last_pos
;
free_lun, unit
if (count eq 0) then return, '' $
else if (count lt num_lines) then return, lines(0:count-1) $
else return, lines
end ; at_get_lines
; -----------------------------------------------------------------------------
;
; Purpose: Build a default template structure.
;
function at_build_template, data, missing_value
; Use the first record from the file (ignoring comment strings
; and blank lines).
;
lines = (*data.p_rlines)(0:n_elements(*data.p_num_fields)-1)
; lut is used to determine if a given field is integer,
; floating point, or a string.
;
lut = bytarr(256) + 1b
lut(0:32) = 0b
lut(48:57) = 0b
lut(byte('e')) = 0
lut(byte('E')) = 0
lut(byte('+')) = 0
lut(byte('-')) = 0
lut(byte('.')) = 0
;
; scan through a sample record and determine a default column
; position and IDL type for each field.
;
tot_num_fields = long(total(*data.p_num_fields))
field_locations = lonarr(tot_num_fields)
field_types = intarr(tot_num_fields)
fpos = 0L
for i=0, n_elements(*data.p_num_fields)-1 do begin
bline = [byte(lines(i)), 32b]
if (data.delimit eq 32b) then begin ; delimiter is 'space'
nptr = where(bline ne 32b and bline ne 9b, ncount)
fptr = nptr(0)
for j=1, ncount-1 do $
if (nptr(j) gt nptr(j-1)+1) then fptr = [fptr, nptr(j)]
fptr = [fptr, n_elements(bline)]
add = [0,1]
endif else begin
nptr = where(bline eq data.delimit, ncount)
if (ncount eq 0) then fptr = [-1, n_elements(bline)] $
else fptr = [-1, nptr, n_elements(bline)]
add = [1,1]
endelse
;
for j=0, (n_elements(fptr)-2)<((*data.p_num_fields)(i)-1) do begin
field_locations(fpos) = fptr(j) + add(0)
bsub = bline(fptr(j)+add(0):(fptr(j+1)-add(1))>(fptr(j)+add(0)))
is_string = (total(lut(bsub)) gt 0)
if (is_string eq 0) then begin
if (total(bsub eq 46b) gt 0 or total(bsub eq 101b) gt 0 or $
total(bsub eq 69b) gt 0) then field_types(fpos) = 4 $
else $
field_types(fpos) = 3
endif else $
field_types(fpos) = 7
fpos = fpos + 1
endfor
endfor
;
s_delimit = ([0b, data.delimit])(data.mode)
template = at_build_templ(num_fields=*data.p_num_fields, $
field_types=field_types, field_locations=field_locations, $
record_start_loc=data.data_start, delimiter=s_delimit, $
missing_value=missing_value, comment_symbol=data.comment)
return, template
end ; at_build_template
; -----------------------------------------------------------------------------
;
; Purpose: Return an array of number of lines long the number of fields
; contained in each record based upon where the number of
; delimiters found.
;
function at_num_fields, lines, delimit, comment
;
for i=0, n_elements(lines)-1 do begin
bline = byte(lines(i))
if (delimit eq 32b) then begin ; delimiter is 'space'
nptr = where(bline ne 32b and bline ne 9b, count)
fptr = nptr(0)
for j=1, count-1 do $
if (nptr(j) gt nptr(j-1)+1) then fptr = [fptr, nptr(j)]
endif else begin
nptr = where(bline eq delimit, count)
fptr = bytarr(count+1)
endelse
;
if (n_elements(num_fields) eq 0) then num_fields = n_elements(fptr) $
else begin
if (n_elements(fptr) eq num_fields(0)) then return, num_fields
num_fields = [num_fields, n_elements(fptr)]
endelse
endfor
if (n_elements(num_fields) gt 0) then return, num_fields $
else return, 1
end ; at_num_fields
; -----------------------------------------------------------------------------
;
; Purpose: Given the first line of data make a guess as to the delimiter
; in use.
;
function at_default_delimit, line, comment
if (comment ne '') then begin
pos = strpos(line, comment, 0)
if (pos(0) ge 0) then line = strmid(line, 0, pos(0)-1)
endif
pos = strpos(line, ',')
if (pos(0) ge 0) then return, 44B
pos = strpos(line, ';')
if (pos(0) ge 0) then return, 59B
; dont assume that the delimiter can be a colon...
; pos = strpos(line, ':')
; if (pos(0) ge 0) then return, 58B
; Default return 'space' as the delimiter.
;
return, 32B
end ; at_default_delimit
; -----------------------------------------------------------------------------
;
; Purpose: Return a best guess of the groupings of data base upon initial
; data type.
;
function at_default_groups, data
types = (*data.p_fields).type
groups = intarr(n_elements(types))
cur_group = 0
for i=1, n_elements(types)-1 do begin
if (types(i) eq types(i-1)) then groups(i) = groups(i-1) $
else begin
cur_group = cur_group + 1
groups(i) = cur_group
endelse
endfor
return, groups
end ; at_default_groups
; -----------------------------------------------------------------------------
;
; Purpose: Given a position between 0 and tot_num_fields-1 determine
; the line and location on the line of the given field.
;
function at_which_field, num_fields, pos, col
;
count = 0
for i=0, n_elements(num_fields)-1 do begin
if (pos lt count+num_fields(i)) then begin
col = pos - count
return, i
endif
count = count + num_fields(i)
endfor
end ; at_which_field
; -----------------------------------------------------------------------------
;
; Purpose: Convert a scaler string into a long or floating point value,
; return 0 for any problems.
;
function at_str_to_val, str, floating=floating
;
catch, error_status
if (error_status ne 0) then return, 0
if (keyword_set(floating)) then temp = 0. $
else temp = 0L
reads, str(0), temp
return, temp
end ; at_str_to_val
; -----------------------------------------------------------------------------
;
; Purpose: Convert an array of numbers into a string of comma sepearted
; values.
;
function at_list_to_str, vals
;
str = strtrim(string(vals(0)),2)
for i=1, n_elements(vals)-1 do $
str = str + ',' + strtrim(string(vals(i)),2)
return, str
end ; at_list_to_str
; -----------------------------------------------------------------------------
;
; Purpose: Given a string of comma separated values, convert into an array
; of values.
;
function at_str_to_list, str
;
len = strlen(str(0))
ptr = where(byte(str(0)) eq 44b, count)
sub = (strmid(str(0),len-1,1) eq ',')
count = count - sub
vals = lonarr(count+1)
vals(0) = at_str_to_val(str(0))
for i=0, count-1 do $
vals(i+1) = at_str_to_val(strmid(str(0),ptr(i)+1,len))
return, vals
end ; at_str_to_list
; -----------------------------------------------------------------------------
;
; Purpose: Scan through the string array and replace any instance of
; tab (9b) with four white spaces.
;
pro at_remove_tabs, lines
s_tab = string(9b)
for i=0, n_elements(lines)-1 do begin
loc = 0l
len = strlen(lines(i))
repeat begin
pos = strpos(lines(i), s_tab, loc)
if (pos ge 0) then begin
lines(i) = strmid(lines(i), 0, pos) + ' ' + $
strmid(lines(i), pos+1, len)
loc = pos + 1
endif
endrep until (pos eq -1)
endfor
end ; at_remove_tabs
; -----------------------------------------------------------------------------
;
; Purpose: Display the appropriate text into the main table widget.
;
pro at_display_text, data, first=first
if (keyword_set(first)) then begin
top_pos = widget_info(data.tw(6), /table_view)
num_lines = n_elements(*data.p_lines)
rstr = strtrim(string(indgen(num_lines)+1),2)
if (total(byte(*data.p_lines) eq 9b) gt 0) then begin
lines = *data.p_lines
at_remove_tabs, lines
lines = reform(lines, 1, num_lines, /overwrite)
endif else $
lines = reform(*data.p_lines, 1, num_lines)
widget_control, data.tw(6), set_table_view=top_pos, set_value=lines, $
row_labels=rstr, set_table_select=[0,data.data_start<(data.twm(1)-1), $
0,data.data_start<(data.twm(1)-1)]
endif else begin
num_lines = n_elements(*data.p_rlines)
rstr = strtrim(string(indgen(num_lines)+1),2)
if (total(byte(*data.p_rlines) eq 9b) gt 0) then begin
lines = *data.p_rlines
at_remove_tabs, lines
lines = reform(lines, 1, num_lines, /overwrite)
endif else $
lines = reform(*data.p_rlines, 1, num_lines)
widget_control, data.tw(6), set_table_view=[0,0], set_value=lines, $
row_labels=rstr, set_table_select=[-1,-1,-1,-1]
endelse
end ; at_display_text
; -----------------------------------------------------------------------------
;
; Purpose: Resize a given table widget to a new number of row / col cells.
;
pro at_resize_table, tw, prev_size, new_size, text_table=text_table, last=last
;
; there are two different table widgets used in this widget. if the text_table
; keyword is set, then this refers to the table widget on the bottom which
; displays the text in STEP 1, the data in STEP 2 and a sample record in
; STEP 3, if not set, then this refers to the table wdiget in STEP 3 in the
; upper left corner which displays the field information.
;
if (prev_size(0) eq new_size(0) and prev_size(1) eq new_size(1)) then return
;
col_diff = prev_size(0) - new_size(0)
row_diff = prev_size(1) - new_size(1)
;
if (prev_size(1)-new_size(1) gt 0) then $
widget_control, tw, delete_rows=prev_size(1)-new_size(1), $
use_table_select=[0,new_size(1),prev_size(0)-1,prev_size(1)-1] $
else if (prev_size(1)-new_size(1) lt 0) then $
widget_control, tw, insert_rows=-(prev_size(1)-new_size(1))
;
if (col_diff gt 0) then $
widget_control, tw, delete_columns=col_diff, $
use_table_select=[new_size(0),0,prev_size(0)-1,new_size(1)-1] $
else if (col_diff lt 0) then $
widget_control, tw, insert_columns=-col_diff
;
if (keyword_set(text_table)) then begin
; the text table has a different look in STEP 3 then in STEP 1 & 2...
;
if (keyword_set(last)) then $
; widget_control, tw, column_widths=.75, units=1 $
widget_control, tw, column_widths=90 $
else begin
widget_control, tw, column_widths=450, column_labels=['Text']
widget_control, tw, column_widths=40, use_table_select=[-1,0,-1,0]
endelse
endif
end ; at_resize_table
; -----------------------------------------------------------------------------
;
; Purpose: Organize and display a sample record of n lines to demonstrate
; how the current defined template interprets the ASCII file.
;
pro at_sample_record, data
num_fields = *data.p_num_fields
lines = (*data.p_rlines)(0:n_elements(num_fields)-1)
;
new_twm = [max(num_fields), n_elements(num_fields)]
at_resize_table, data.tw(6), data.twm, new_twm, /text_table, /last
data.twm = new_twm
str = strarr(new_twm(0), new_twm(1))
fpos = 0
; Loop for each field.
;
for i=0, n_elements(num_fields)-1 do begin
for j=0, num_fields(i)-1 do begin
if (j eq num_fields(i)-1) then $ ; last field
len = strlen(lines(i)) - (*data.p_fields)(fpos).loc $
else $ ; not last field
len = (*data.p_fields)(fpos+1).loc - (*data.p_fields)(fpos).loc - 1
str(j,i) = strtrim(strmid(lines(i), (*data.p_fields)(fpos).loc, len),2)
fpos = fpos + 1
endfor
endfor
widget_control, data.tw(6), set_value=str
end ; at_sample_record
; -----------------------------------------------------------------------------
;
; Purpose: Organize and display the field specifications into a table widget.
;
pro at_set_list, data, just_highlight=just_highlight
groups = *data.p_groups
if (keyword_set(just_highlight) eq 0) then begin
dstr = ['Skip', 'Byte', 'Integer', 'Long', 'Floating', 'Double', $
'Complex', 'String']
new_tws = [ ([3,2])(data.mode), long(total(*data.p_num_fields)) ]
at_resize_table, data.tw(8), data.tws, new_tws
data.tws = new_tws
; String array, set to [3,1] in at_widget.
; [0,0] =
;
str = strarr(data.tws(0), data.tws(1))
gptr = 0
; Loop for each group.
;
for i=0, n_elements(groups)-1 do begin
if (i eq 0) then new_group = 1 $
else new_group = (groups(i) ne groups(i-1))
ptr = where(groups eq groups(i), count)
if (new_group) then begin
gptr = i
if (count eq 1) then str(0,i) = (*data.p_fields)(i).name $
else str(0,i) = '{1} ' + (*data.p_fields)(i).name
str(1,i) = dstr((*data.p_fields)(i).type)
if (data.mode eq 0) then $
str(2,i) = strtrim(string((*data.p_fields)(i).loc),2)
endif else begin
str(0,i) = '{' + strtrim(string(i-gptr+1),2) + '}'
if (data.mode eq 0) then $
str(2,i) = strtrim(string((*data.p_fields)(i).loc),2)
endelse
endfor
if (n_elements(*data.p_num_fields) eq 1) then $
c_str = reform(str(0,*)) $
else $
c_str = strarr(data.twm(0)) + ' '
widget_control, data.tw(6), column_label=c_str
rstr = strtrim(string(indgen(data.tws(1))+1),2)
widget_control, data.tw(8), set_value=str, row_labels=rstr, alignment=0
endif
widget_control, data.tw(8), set_table_select= $
[0, data.lptr, data.tws(0)-1, data.lptr]
lpos = at_which_field(*data.p_num_fields, data.lptr, col)
widget_control, data.tw(6), set_table_select=[col,lpos,col,lpos]
ptr = where(groups eq groups(data.lptr), count)
widget_control, data.dl, sensitive=(ptr(0) eq data.lptr)
widget_control, data.buts(14), sensitive=(ptr(0) eq data.lptr and count gt 1)
end ; at_set_list
; -----------------------------------------------------------------------------
;
; Purpose:
;
pro at_update, data, new_lptr, change=change, new=new
if (n_elements(change) eq 0) then change = 0
if (keyword_set(new) eq 0) then begin
widget_control, data.tw(4), get_value=name
widget_control, data.tw(5), get_value=col
change = change or (name(0) ne (*data.p_fields)(data.lptr).name or $
long(col(0)) ne (*data.p_fields)(data.lptr).loc)
(*data.p_fields)(data.lptr).name = name(0)
(*data.p_fields)(data.lptr).loc = long(col(0))
endif
data.lptr = new_lptr
at_set_list, data, just_highlight=(change eq 0)
widget_control, data.tw(4), set_value=(*data.p_fields)(new_lptr).name
widget_control, data.dl, set_droplist_select= $
(*data.p_fields)(new_lptr).type
widget_control, data.tw(5), set_value= $
strtrim(string((*data.p_fields)(new_lptr).loc),2)
end ; at_update
; -----------------------------------------------------------------------------
;
; Purpose: Handle events for third page of GUI.
;
pro at_3_event, ev
widget_control, ev.id, get_uvalue=uvalue
widget_control, ev.top, get_uvalue=data, /no_copy
if (uvalue eq 'group') then begin
sel = widget_info(data.tw(8), /table_select)
if (sel(1) ne sel(3)) then begin
types = (*data.p_fields)(sel(1):sel(3)).type
is_string = (total(types eq 7) gt 0)
if (is_string) then $
(*data.p_fields)(sel(1):sel(3)).type = (7 * (types ne 0)) $
else $
(*data.p_fields)(sel(1):sel(3)).type = (max(types) * (types ne 0))
(*data.p_groups)(sel(1):sel(3)) = (*data.p_groups)(sel(1))
at_update, data, data.lptr, /change, /new
endif
endif
if (uvalue eq 'ungroup') then begin
ptr = where(*data.p_groups eq (*data.p_groups)(data.lptr))
(*data.p_groups)(ptr) = (indgen(n_elements(*data.p_fields)))(ptr)
(*data.p_fields)(ptr).type = (*data.p_types)(ptr)
at_set_list, data
endif
if (uvalue eq 'ungroup all') then begin
*data.p_groups = indgen(n_elements(*data.p_fields))
(*data.p_fields).type = *data.p_types
at_set_list, data
endif
if (uvalue eq 'ltable') then begin
; if selection event...
if (ev.type eq 4) then $
if (ev.sel_top eq ev.sel_bottom and ev.sel_top ne -1) then $
at_update, data, ev.sel_top
endif
if (uvalue eq 'table') then begin
; if selection event...
if (ev.type eq 4) then $
if (ev.sel_top eq ev.sel_bottom and ev.sel_top ne -1) then begin
tot_num_fields = long(total(*data.p_num_fields))
if (ev.sel_top eq 0) then add = 0 $
else add = long(total((*data.p_num_fields)(0:ev.sel_top-1)))
new_ptr = (ev.sel_left + add) < (tot_num_fields-1)
at_update, data, new_ptr, /change
endif
endif
if (uvalue eq 'list') then begin
if (ev.index lt n_elements(*data.p_fields)) then $
at_update, data, ev.index
endif
if (uvalue eq 'name') then begin
if (ev.type eq 0) then begin
bad = (ev.ch lt 48 or (ev.ch gt 57 and ev.ch lt 65) or $
(ev.ch gt 90 and ev.ch lt 97 and ev.ch ne 95) or ev.ch gt 122l)
if (bad) then begin
widget_control, ev.id, set_value=(*data.p_fields)(data.lptr).name
widget_control, ev.id, set_text_select= $
strlen((*data.p_fields)(data.lptr).name)
endif
endif
if (keyword_set(bad) eq 0) then begin
widget_control, ev.id, get_value=name
if (name(0) ne (*data.p_fields)(data.lptr).name and $
name(0) ne '') then begin
(*data.p_fields)(data.lptr).name = name(0)
at_set_list, data
endif
endif
endif
if (uvalue eq 'type') then begin
ptr = where(*data.p_groups eq (*data.p_groups)(data.lptr), count)
if (count eq 1) then begin
(*data.p_fields)(data.lptr).type = ev.index
(*data.p_types)(data.lptr) = ev.index
endif else $
(*data.p_fields)(ptr).type = (ev.index * ((*data.p_types)(ptr) ne 0))
at_set_list, data
endif
if (uvalue eq 'location') then begin
widget_control, ev.id, get_value=str
(*data.p_fields)(data.lptr).loc = at_str_to_val(str)
at_set_list, data
; Organize and display a sample record of n lines to demonstrate
; how the current defined template interprets the ASCII file.
;
at_sample_record, data
endif
if (strmid(uvalue,0,4) eq 'miss') then begin
data.miss_type = fix(strmid(uvalue,4,1))
widget_control, data.mb(4), sensitive=data.miss_type
if (data.miss_type eq 0) then $
(*data.p_template).missing_value = !values.f_nan $
else begin
widget_control, data.tw(0), get_value=str
(*data.p_template).missing_value = at_str_to_val(str, /floating)
endelse
endif
if (uvalue eq 'value') then begin
widget_control, ev.id, get_value=str
(*data.p_template).missing_value = at_str_to_val(str, /floating)
endif
widget_control, ev.top, set_uvalue=data, /no_copy
end ; at_3_event
; -----------------------------------------------------------------------------
;
; Purpose: Handle events for second page of GUI.
;
pro at_2_event, ev
;
widget_control, ev.id, get_uvalue=uvalue
widget_control, ev.top, get_uvalue=data, /no_copy
if (uvalue eq 'user' or strmid(uvalue,0,7) eq 'delimit') then begin
if (uvalue eq 'user') then type = 5 $
else type = fix(strmid(uvalue,7,1))
data.delimit = ([9,59,32,44,58,32])(type)
widget_control, data.mb(5), sensitive=(type eq 5)
if (type eq 5) then begin
widget_control, data.tw(1), get_value=str
data.delimit = (byte(strmid(str(0),0,1)))(0)
endif
;
num_fields = at_num_fields(*data.p_rlines, data.delimit, data.comment)
widget_control, data.tw(2), set_value=at_list_to_str(num_fields)
*data.p_num_fields = temporary(num_fields)
data.change = 1
endif
if (uvalue eq 'fields') then begin
widget_control, ev.id, get_value=str
if (strtrim(strcompress(str(0)),2) eq '') then str = '1'
*data.p_num_fields = at_str_to_list(str)
data.change = 1
endif
widget_control, ev.top, set_uvalue=data, /no_copy
end ; at_2_event
; -----------------------------------------------------------------------------
;
; Purpose: Handle events for first page of GUI.
;
pro at_1_event, ev
widget_control, ev.id, get_uvalue=uvalue
widget_control, ev.top, get_uvalue=data, /no_copy
if (uvalue eq 'text') then begin
widget_control, ev.id, get_value=str
ds = (at_str_to_val(str)-1)
if (ds ne data.data_start) then begin
data.data_start = ds
widget_control, data.tw(6), set_table_select= $
[0,data.data_start<(data.twm(1)-1),0,data.data_start<(data.twm(1)-1)]
data.change = 1
endif
endif
if (uvalue eq 'comment') then begin
widget_control, ev.id, get_value=str
data.comment = str(0)
data.change = 1
endif
if (strmid(uvalue,0,4) eq 'mode') then begin
data.mode = fix(strmid(uvalue,4,1))
data.change = 1
endif
; Handle table events.
;
if (uvalue eq 'table') then begin
; if selection event...
if (ev.type eq 4) then $
if (ev.sel_left eq 0) then begin
widget_control, data.tw(7), set_value= $
strtrim(string(ev.sel_top+1),2)
data.change = 1
endif
;
; Handle "Next n Lines" button event.
;
endif else if (uvalue eq 'next set') then begin
widget_control, /hourglass
last_pos = data.last_pos
new_lines = at_get_lines(data.name, data.browseLines, $
last_pos=last_pos, end_reached=end_reached)
data.last_pos = last_pos
data.end_reached = end_reached
widget_control, data.mb(7), sensitive=(end_reached eq 0)
if (n_elements(new_lines) gt 1 or new_lines(0) ne '') then begin
*data.p_lines = [*data.p_lines, new_lines]
widget_control, data.tw(6), insert_rows=n_elements(new_lines)
at_display_text, data, /first
endif
endif
widget_control, ev.top, set_uvalue=data, /no_copy
end ; at_1_event
; -----------------------------------------------------------------------------
;
; Purpose: Control the setting of the three progessive states of the
; template definition - sets all of the widgets and pointers
; involved and allows movement both forward and backward in
; the definition process.
;
pro at_set_state, data, forward=forward, back=back
;
case data.step of
; ----------------------------------------
0: begin
; ----------------------------------------
title = 'STEP 1 of 3: Define Data Type / Range'
widget_control, data.base, tlb_set_title=title
widget_control, data.buts(9), sensitive=0
widget_control, data.buts(0), set_button=(data.mode eq 0)
widget_control, data.buts(1), set_button=data.mode
widget_control, data.buts(13), sensitive=0
;
widget_control, data.buts(2), set_button=(data.miss_type eq 0)
widget_control, data.buts(3), set_button=data.miss_type
widget_control, data.mb(4), sensitive=data.miss_type
;
widget_control, data.slab, set_value='Selected Text File'
widget_control, data.mb(7), sensitive=(data.end_reached eq 0), map=1
widget_control, data.tw(7), set_value= $
strtrim(string(data.data_start+1),2)
widget_control, data.tw(6), event_pro='at_1_event'
at_display_text, data, /first
data.change = 0
end
; ----------------------------------------
1: begin
; ----------------------------------------
; retreive the data_start value
widget_control, data.tw(7), get_value=str
data.data_start =(at_str_to_val(str)-1) > 0
;
; scan through lines and remove comments and blank lines for
; steps two and three (after the start of the data).
;
if (keyword_set(forward)) then begin
lines = *data.p_lines
rlines = strarr(n_elements(lines))
count = 0
for i=data.data_start, n_elements(lines)-1 do begin
line = lines(i)
if (data.comment ne '') then begin
pos = strpos(line, data.comment)
if (pos(0) ne -1) then line = strmid(line, 0, pos(0)-1)
endif
if (strtrim(line,2) ne '') then begin
rlines(count) = line
count = count + 1
endif
endfor
if (count gt 0) then *data.p_rlines = rlines(0:count-1) $
else *data.p_rlines = ''
endif
;
widget_control, data.mb(3), map=data.mode
widget_control, data.buts(9), sensitive=1
widget_control, data.buts(10), sensitive=1
widget_control, data.buts(13), sensitive=0
widget_control, data.mb(7), map=0
widget_control, data.slab, set_value='Selected Text Records'
if (data.mode eq 1) then begin
if (keyword_set(forward)) then $
data.delimit = at_default_delimit((*data.p_rlines)(0), data.comment)
title = 'STEP 2 of 3: Define Delimiter / Fields'
widget_control, data.base, tlb_set_title=title
user_defined = (total([59b,58b,32b,44b] eq data.delimit) eq 0)
if (user_defined) then tstr = string(data.delimit) $
else tstr = ''
widget_control, data.tw(1), set_value=tstr
widget_control, data.mb(5), sensitive=user_defined
widget_control, data.buts(5), set_button=(data.delimit eq 59b)
widget_control, data.buts(6), set_button=(data.delimit eq 32b)
widget_control, data.buts(7), set_button=(data.delimit eq 44b)
widget_control, data.buts(11),set_button=(data.delimit eq 9b)
widget_control, data.buts(16), set_button=(data.delimit eq 58b)
widget_control, data.buts(8), set_button=user_defined
endif else begin
title = 'STEP 2 of 3: Define Fields'
widget_control, data.base, tlb_set_title=title
endelse
;
if ((keyword_set(forward) and data.change) or $
n_elements(*data.p_num_fields) eq 0) then $
num_fields = at_num_fields(*data.p_rlines, data.delimit,data.comment) $
else begin
num_fields = *data.p_num_fields
new_twm = [2, n_elements(*data.p_lines)]
at_resize_table, data.tw(6), data.twm, new_twm, /text_table
data.twm = new_twm
endelse
;
widget_control, data.tw(2), set_value=at_list_to_str(num_fields)
*data.p_num_fields = temporary(num_fields)
;
widget_control, data.tw(6), event_pro='at_2_event'
at_display_text, data
data.change = 0
end
; ----------------------------------------
2: begin
; ----------------------------------------
title = 'STEP 3 of 3: Field Specification'
widget_control, data.base, tlb_set_title=title
widget_control, data.buts(10), sensitive=0
widget_control, data.mb(6), map=(data.mode eq 0)
widget_control, data.buts(13), sensitive=1
widget_control, data.slab, set_value='Sample Record'
;
if (data.miss_type eq 0) then $
miss_value = !values.f_nan $
else begin
widget_control, data.tw(0), get_value=str
miss_value = at_str_to_val(str)
endelse
;
if (n_elements(*data.p_template) eq 0 or data.change) then begin
; build new template
;
template = at_build_template(data, miss_value)
; delete previous template
;
at_delete_template, *data.p_template
; save new template
;
*data.p_template = temporary(template)
; assign a copy of pointers from the template to the data structure
; for simpler access.
;
data.p_fields = (*data.p_template).p_fields
data.p_groups = (*data.p_template).p_groups
*data.p_types = (*data.p_fields).type
endif
; Organize and display a sample record of n lines to demonstrate
; how the current defined template interprets the ASCII file.
;
at_sample_record, data
widget_control, data.tw(6), event_pro='at_3_event'
data.lptr = 0
at_update, data, data.lptr, /change, /new
end
endcase
end ; at_set_state
; -----------------------------------------------------------------------------
;
; Purpose:
;
pro at_widget_cleanup, base
;
widget_control, base, get_uvalue=data, /no_copy
widget_control, base, /destroy
if (n_elements(data) eq 0) then return
;
ptr_free, data.p_lines
ptr_free, data.p_rlines
ptr_free, data.p_num_fields
ptr_free, data.p_template
ptr_free, data.p_types
end ; at_widget_cleanup
; -----------------------------------------------------------------------------
;
; Purpose:
;
pro at_widget_event, ev
;
if (tag_names(ev,/struct) eq 'WIDGET_KILL_REQUEST') then begin
at_widget_cleanup, ev.top
return
endif
;
widget_control, ev.id, get_uvalue=uvalue
widget_control, ev.top, get_uvalue=data, /no_copy
if (uvalue eq 'next') then begin
widget_control, data.mb(data.step), map=0
data.step = data.step + 1
widget_control, data.mb(data.step), map=1
at_set_state, data, /forward
endif
if (uvalue eq 'back') then begin
widget_control, data.mb(data.step), map=0
data.step = data.step - 1
widget_control, data.mb(data.step), map=1
at_set_state, data, /back
endif
if (uvalue eq 'finish') then begin
*data.p_result = {accept:1, template:*data.p_template}
widget_control, ev.top, set_uvalue=data, /no_copy
at_widget_cleanup, ev.top
return
endif
if (uvalue eq 'cancel') then begin
at_delete_template, *data.p_template
widget_control, ev.top, set_uvalue=data, /no_copy
at_widget_cleanup, ev.top
return
endif
widget_control, ev.top, set_uvalue=data, /no_copy
end ; at_widget_event
; -----------------------------------------------------------------------------
;
; Purpose:
;
function at_widget, $
name, $ ; IN:
GROUP=group, $ ; IN: (opt)
CANCEL=cancel, $ ; OUT:
BROWSE_LINES=browseLines ; IN:
; Set number of lines for browse button.
;
if (N_ELEMENTS(browseLines) ne 0) then browseLinesUse = browseLines $
else browseLinesUse = 50
; check out the size of the screen and adjust the widget acordingly
device, get_screen_size=screen_size
table_scr_ysize = ([110,210])(screen_size(1) gt 600)
xoff = ((screen_size(0) - 600) / 2) > 0
yoff = ((screen_size(1) - 600) / 2) > 0
dt_str = ['Skip Field', 'Byte', 'Integer', 'Long Integer', $
'Floating Point', 'Double Precision', 'Complex', 'String']
lines = at_get_lines(name, browseLinesUse, last_pos=last_pos, $
end_reached=end_reached)
buts = lonarr(17)
tw = lonarr(10)
mb = lonarr(8)
title = 'STEP 1 of 3: Define Data Type / Range'
; Create a group if one wasn't specified.
if (N_ELEMENTS(group) eq 0) then begin
group = widget_base()
groupCreated = 1B
endif else $
groupCreated = 0B
base = widget_base(title=title, /column, xoff=xoff, yoff=yoff, $
/modal, group=group)
sb = widget_base(base, /column)
mbs = widget_base(sb)
;
; STEP 1 of 3: Define Data Type / Range
;
mb(0)= widget_base(mbs, /column, event_pro='at_1_event')
sb = widget_base(mb(0), /column, /frame)
lab = widget_label(sb, $
value='Choose the field type which best describes your data:')
lab = widget_label(sb, value='')
sb1 = widget_base(sb, /column, /exclusive)
buts(0)= widget_button(sb1, /no_release, uvalue='mode0', $
value=' Fixed Width (fields are aligned in columns)')
buts(1)= widget_button(sb1, /no_release, uvalue='mode1', $
value=' Delimited (commas, whitespace, etc. separate each field)')
;
sb = widget_base(mb(0), /row, /frame)
lab = widget_label(sb, value=' Comment String to Ignore: ')
tw(9)= widget_text(sb, xs=10, ys=1, /edit, frame=0, /all_events, $
uvalue='comment')
;
sb = widget_base(mb(0), /row, /frame)
lab = widget_label(sb, value=' Data Starts at Line: ')
tw(7)= widget_text(sb, xs=5, ys=1, /edit, frame=0, /all_events, $
uvalue='text')
;
; STEP 2 of 3: Define Delimiter / Fields
;
mb(1)= widget_base(mbs, /column, map=0, event_pro='at_2_event')
sb = widget_base(mb(1), /column, /frame)
sb1 = widget_base(sb, /row)
lab = widget_label(sb1, value='Number of Fields Per Line: ')
tw(2)= widget_text(sb1, xs=10, ys=1, frame=0, /edit, /all_events, $
uvalue='fields')
;
mb(3)= widget_base(mb(1), /column, /frame)
lab = widget_label(mb(3), value='Delimiter Between Data Elements:')
sb1 = widget_base(mb(3), /row)
sb2 = widget_base(sb1, row=2, /exclusive)
mb(5)= widget_base(sb1, /row, /align_bottom)
tw(1)= widget_text(mb(5), xs=2, ys=1, /edit, frame=0, $
/all_events, uvalue='user')
buts(6) = widget_button(sb2, value='White Space', uvalue='delimit2', /no_rel)
buts(7) = widget_button(sb2, value='Comma', uvalue='delimit3', /no_rel)
buts(16)= widget_button(sb2, value='Colon', uvalue='delimit4', /no_rel)
buts(5) = widget_button(sb2, value='Semicolon', uvalue='delimit1', /no_rel)
buts(11)= widget_button(sb2, value='Tab', uvalue='delimit0', /no_rel)
buts(8) = widget_button(sb2, value='Other:', uvalue='delimit5', /no_rel)
;
; STEP 3 of 3: Field Specifications
;
mb(2)= widget_base(mbs, /column, map=0, event_pro='at_3_event')
sb = widget_base(mb(2), column=2)
sb1 = widget_base(sb, /column, /frame)
tw(8)= widget_table(sb1, value=strarr(3,1), alignment=0, scr_xsize=225, $
scr_ysize=150, uvalue='ltable', column_widths=[100,75,50], $
column_labels=['Name','Data Type','Loc'], /ALL_EVENTS, /SCROLL, $
/RESIZEABLE_COLUMNS)
widget_control, tw(8), column_widths=25, use_table_select=[-1,0,-1,0]
;
sb1 = widget_base(sb, /column, /frame)
sb2 = widget_base(sb1, /row)
lab = widget_label(sb2, value='Name: ')
tw(4)= widget_text(sb2, xs=18, ys=1, frame=0, /edit, /all_events, $
uvalue='name')
sb2 = widget_base(sb1, /row)
dl = widget_droplist(sb2, Title='Type: ', value=dt_str, uvalue='type')
mb(6)= widget_base(sb1, /row)
lab = widget_label(mb(6), value='Column')
tw(5)= widget_text(mb(6), xs=3, ys=1, frame=0, /edit, /all_events, $
uvalue='location')
sb2 = widget_base(sb1, /row)
but = widget_button(sb2, value=' Group ', uvalue='group')
buts(14) = widget_button(sb2, value=' UnGroup ', uvalue='ungroup')
but = widget_button(sb2, value=' Ungroup All ', uvalue='ungroup all')
;
sb = widget_base(mb(2), /row, /frame)
lab = widget_label(sb, value=' Assign Missing Data: ')
sb1 = widget_base(sb, /row)
sb2 = widget_base(sb1, /row, /exclusive)
buts(2)= widget_button(sb2, value='IEEE NaN', /no_rel, uvalue='miss0')
buts(3)= widget_button(sb2, value='Value:', /no_rel, uvalue='miss1')
mb(4) = widget_base(sb1, /row)
tw(0)= widget_text(mb(4), xs=7, ys=1, frame=0, /edit, /all_events, $
uvalue='value')
;
; general text widget and <back, next> buttons
;
sb = widget_base(base, /column)
sb1 = widget_base(sb, /row)
slab = widget_label(sb1, value='Selected Text File ')
mb(7)= widget_base(sb1, /row)
widget_control, mb(7), sensitive=(end_reached eq 0)
value = ' Read in Next ' + STRTRIM(STRING(browseLinesUse),2) + ' Lines '
but = widget_button(mb(7), value=value, uvalue='next set', $
event_pro='at_1_event')
str = strarr(1,n_elements(lines))
tw(6)= widget_table(sb, value=str, alignment=0, scr_xsize=500, $
scr_ysize=table_scr_ysize, uvalue='table', column_labels=['Text'], $
column_widths=450, /ALL_EVENTS, /SCROLL, /RESIZEABLE_COLUMNS)
widget_control, tw(6), column_widths=40, use_table_select=[-1,0,-1,0]
;
sb = widget_base(base, /row, /align_right)
sb1 = widget_base(sb, /row, /frame)
but = widget_button(sb1, value=' Cancel ', uvalue='cancel')
buts(9) = widget_button(sb1, value=' < Back ', uvalue='back')
buts(10)= widget_button(sb1, value=' Next > ', uvalue='next')
buts(13)= widget_button(sb1, value=' Finish ', uvalue='finish')
widget_control, base, /realize
widget_control, tw(8), /input_focus
;
p_result = ptr_new({accept:0})
data = {base:base, mb:mb, tw:tw, buts:buts, dl:dl, step:0, name:name, $
p_lines:ptr_new(lines), p_rlines:ptr_new(/allocate_heap), $
p_num_fields:ptr_new(/allocate_heap), p_template:ptr_new(/allocate_heap), $
p_types:ptr_new(/allocate_heap), p_fields:ptr_new(), p_groups:ptr_new(), $
p_result:p_result, mode:1, data_start:0L, lptr:0, delimit:32b, $
miss_type:0, last_pos:last_pos, change:1, end_reached:end_reached, $
comment:'', twm:[1,n_elements(lines)], tws:[3,1], slab:slab, $
browseLines:browseLinesUse}
at_set_state, data
widget_control, base, set_uvalue=data, /no_copy
xmanager, 'at_widget', base
;
if (groupCreated) then $
widget_control, group, /destroy
cancel = ((*p_result).accept eq 0)
if ((*p_result).accept) then template = (*p_result).template $
else template = 0
ptr_free, p_result
return, template
end ; at_widget
; -----------------------------------------------------------------------------
;
; Purpose: Check that the input filename is a string, exists, and appears
; to be ASCII...
;
function at_check_file, fname
catch, error_status
if (error_status ne 0) then begin
if (n_elements(unit) gt 0) then free_lun, unit
return, -3 ; unexpected error reading from file
endif
;
info = size(fname)
if (info(info(0)+1) ne 7) then return, -1 ; filename isn't a string
;
openr, unit, fname, error=error, /get_lun
if (error eq 0) then begin
finfo = fstat(unit)
; set non-ascii values in lookup table
lut = bytarr(256) + 1b
lut[7:13] = 0b
lut[32:127] = 0b
data = bytarr(32767<finfo.size, /nozero)
readu, unit, data
free_lun, unit
carriage_return = (total(data eq 10b) gt 0 or total(data eq 13b) gt 0)
if (carriage_return eq 0) then return, -4 ; looks like a binary file
non_printable = (total(lut(data)) gt 0)
if (non_printable) then return, -4 $ ; looks like a binary file
else return, 0 ; everything is cool
endif else $
return, -2 ; unable to open file
end
; -----------------------------------------------------------------------------
;
; Purpose: The main routine.
;
function ascii_template, $
file, $ ; IN: (opt)
BROWSE_LINES=browseLines, $ ; IN: (opt)
GROUP=group, $ ; IN: (opt)
CANCEL=cancel ; OUT: (opt)
; Set to return to caller on error.
;
ON_ERROR, 2
; ON_ERROR, 0
; Set some defaults.
;
cancel = 0
; Set number of lines for browse button.
;
if (N_ELEMENTS(browseLines) ne 0) then browseLinesUse = browseLines $
else browseLinesUse = 50
; If no file specified, use DIALOG_PICKFILE.
;
if (n_elements(file) eq 0) then begin
file = DIALOG_PICKFILE(/MUST_EXIST, GROUP=group)
if (file eq '') then RETURN, 0
endif
; check that the file is readable and appears to be ASCII
;
ret = at_check_file(file)
case ret of
-1: MESSAGE, 'File name must be a string.'
-2: MESSAGE, 'File "' + file + '" not found.'
-3: MESSAGE, 'Error Reading from file "' + file + '"
-4: MESSAGE, 'File "' + file + '" is not an ASCII file.'
else:
endcase
; Put up the GUI.
;
templateWithPtrs = $
at_widget(file, cancel=cancel, BROWSE_LINES=browseLinesUse, GROUP=group)
; If user canceled, return 0.
;
if (cancel) then RETURN, 0
; Restructure template to eliminate pointers and return it.
; (Include a version number for easier processing if modify
; the template definition later.)
;
template = { $
version: 1.0, $
dataStart: templateWithPtrs.record_start_loc, $
delimiter: templateWithPtrs.delimit, $
missingValue: templateWithPtrs.missing_value, $
commentSymbol: templateWithPtrs.comment_symbol, $
fieldCount: *templateWithPtrs.p_num_fields, $
fieldTypes: (*templateWithPtrs.p_fields).type, $
fieldNames: (*templateWithPtrs.p_fields).name, $
fieldLocations: (*templateWithPtrs.p_fields).loc, $
fieldGroups: *templateWithPtrs.p_groups $
}
at_delete_template, templateWithPtrs
RETURN, template
end ; ascii_template
; -----------------------------------------------------------------------------